/****************************************************************************
 * Copyright 2010 Huascar A. Sanchez                                        *
 *                                                                          *
 * Licensed under the Apache License, Version 2.0 (the "License");          *
 * you may not use this file except in compliance with the License.         *
 * You may obtain a copy of the License at                                  *
 *                                                                          *
 *     http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                          *
 * Unless required by applicable law or agreed to in writing, software      *
 * distributed under the License is distributed on an "AS IS" BASIS,        *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 * See the License for the specific language governing permissions and      *
 * limitations under the License.                                           *
 ****************************************************************************/
package com.gotobject.greatq;

import com.gotobject.greatq.datastore.DatastoreConnection;
import com.gotobject.greatq.datastore.DatastoreSQLParser;
import com.gotobject.greatq.datastore.DatastoreStatement;
import org.jiql.jdbc.ResultSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

import static com.gotobject.greatq.ModuleMaker.makeModule;
import static com.gotobject.greatq.ModuleMaker.multipleModules;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;

/**
 * @author huascar.sanchez@gmail.com (Huascar A. Sanchez)
 */
public class OfflineQueryServiceTest {
    @Before
    public void setUp() throws Exception {
        GreatQ.assignConnectionFactory(mockConnectionFactory());
    }

    @Test(expected = QueryingException.class)
    public void testQueryWithoutKey() throws Exception {
        final QueryService p = GreatQ.createQueryService(new AbstractModule(){
            @Override
            protected void configure() {
                submit("Select * from teste");
            }
        });

        p.fetchResult(null);
    }

    @Test
    public void testSingleOfflineQuery() throws Exception {
        GreatQ.assignConnectionFactory(mockConnectionFactory());
        final QueryService o = GreatQ.createQueryService(new AbstractModule(){
            @Override
            protected void configure() {
                submit("Select * from teste").as("offline");
            }
        });

        assertNotNull("get actual value", o.fetchResult("offline"));
    }

    @Test
    public void testSubmittedQueriesCount() throws Exception {
        final QueryService       service        = GreatQ.createQueryService(
                multipleModules(
                    makeModule("select * from table1", "qr1"),
                    makeModule("select * from table2", "qr2"),
                    makeModule("select * from table3", "qr3"),
                    makeModule("select * from table4", "qr4"),
                    makeModule("select * from table5", "qr5")
                )
        );
        final Map<String, Query> internalMap    = service.getKeyQueryMap();
        assertSame("5 queries were submitted", 5, internalMap.size());
    }

    @After
    public void tearDown() throws Exception {
        GreatQ.assignConnectionFactory(null);
    }

    private static ConnectionFactory mockConnectionFactory() {
        return new ConnectionFactory(){
            @Override
            public Connection createConnection(String url, Properties props) {
                return new DatastoreConnection(){
                    @Override
                    public Statement createStatement() throws SQLException {
                        return new DatastoreStatement(this){
                            @Override
                            public int[] executeBatch() throws SQLException {
                                return new int[0];
                            }

                            @Override
                            public boolean execute(String sql) throws SQLException {
                                updateList();
                                return true;
                            }

                            private void updateList(){
                                try {
                                    final Field  e  = getClass().getDeclaredField("allResults");
                                    e.setAccessible(true);
                                    final Method a  = CopyOnWriteArrayList.class.getDeclaredMethod("addIfAbsent", Object.class);
                                    a.invoke(e, new ResultSet(
                                            new Vector<Object>(),
                                            new DatastoreSQLParser(
                                                    "nothing",
                                                    (DatastoreConnection) getConnection()
                                            )
                                    ));
                                } catch (Exception e1) {
                                    throw new Error();
                                }

                            }

                            @Override
                            public ResultSet executeQuery(String sql) throws SQLException {
                                return new ResultSet(
                                        new Vector<Object>(),
                                        new DatastoreSQLParser(
                                                "nothing",
                                                (DatastoreConnection) getConnection()
                                        )
                                );
                            }
                        };
                    }
                };
            }
        };
    }

}
